﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.Playwright;
using System.Threading.Tasks;
using PlaywrightStealth;
using System.IO;
using Sy.Net;

namespace BrowserCollect
{
    public class BrowserCollector
    {
        private static IPlaywright? _playwright = null;
        private static IBrowserContext? _browserContext = null;
        private static IPage? _page = null;
        public static string UserDir = "./browser";
        
        /// <summary>
        /// 标记页面是否为登录页
        /// </summary>
        public const string NeedLoginFlag = "NeedLoginFlag";
        
        /// <summary>
        /// 浏览器是否显示
        /// </summary>
        private static bool _isShow = false;

        /// <summary>
        /// 当前使用的userProfile
        /// </summary>
        private static string _currentUserProfile = "";
        private static bool _currentHeadless = true;

        public static string CurrentUserProfile => _currentUserProfile;

        private static IReadOnlyList<BrowserContextCookiesResult>? _allCookies = null;
        private static Task _syncCookiesBackground;

        static BrowserCollector()
        {
            //_syncCookiesBackground = Task.Run(async () =>
            //{
            //    while (true)
            //    {
            //        if (_browserContext is { Browser.IsConnected: true })
            //        {
            //            _allCookies = await _browserContext.CookiesAsync();
            //        }
            //        await Task.Delay(200);
            //    }
            //});
        }

        /// <summary>
        /// 初始化playwright
        /// </summary>
        public static void Init(bool? headless = null, string? profileDirectory = null)
        {
            try
            {
                var init = Task.Run(() => InitAsync(headless: headless, profileDirectory: profileDirectory));
                Task.WaitAll(init);
            } catch (Exception ex)
            {
                throw ex.InnerException;
            }
        }
        
        public static async Task InitAsync(bool? headless = null, string? profileDirectory = null)
        {
            _playwright ??= await Playwright.CreateAsync();
            if (_browserContext is null)
            {
                _allCookies = null;
                _browserContext = await TryLaunchAsync("chrome",headless: headless, profileDirectory: profileDirectory) 
                                    ?? await TryLaunchAsync("chrome", headless: headless, profileDirectory: profileDirectory);
            }
            // _page = _browser?.NewPageAsync().Result;
            if (_page is null)
            {
                _page = _browserContext!.Pages.FirstOrDefault() ?? await _browserContext.NewPageAsync();
            }
            
        }

        private static async Task<IBrowserContext?> TryLaunchAsync(string channel = "msedge", bool? headless = null, IPlaywright? customPlaywright = null, string? profileDirectory = null)
        {
            try
            {
                var playwright = customPlaywright ?? _playwright;
                if (playwright is not null)
                {
                    var userProfile = profileDirectory ?? (!string.IsNullOrWhiteSpace(_currentUserProfile) ? _currentUserProfile : "Default");
                    var tmpHeadless = headless ?? _currentHeadless;
                    var browser = await playwright.Chromium.LaunchPersistentContextAsync(UserDir, new()
                    {
                        UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
                        Channel = channel, // chrome, msedge
// #if DEBUG
//                        Headless = false,
//#else
                           Headless = tmpHeadless,
//#endif
                        // ViewportSize = ViewportSize.NoViewport,
                        // 使用默认窗口大小，天猫详情des无法加载（懒加载）
                        ViewportSize = new ViewportSize(){Width = 1920, Height = 1080},
                        ChromiumSandbox = false,
                        IgnoreDefaultArgs = new[]
                        {
                            "--enable-automation",
                            "--incognito",
                            "--inprivate",
                        },
                        // chrome ”继续上次会话“命令行（销毁后创建浏览器新实例，新实例保持上次关闭的标签页）
                        Args = new string[]
                        {
                            "--disable-web-security",
                            "--disable-restore-session-state",
                            $"--profile-directory={userProfile}"
                        }
                    });
                    _currentUserProfile = userProfile;
                    _currentHeadless = tmpHeadless;

                    await browser.StealthAsync();
                    // TODO 禁用图片节省内存
                    // await browser.RouteAsync("**/*.{png,jpg,jpeg}", r => r.AbortAsync());
                    return browser;
                }
            }
            catch
            {
                throw;
            }

            return null;
        }

        /// <summary>
        /// 获取指定url的dom
        /// </summary>
        public static string GetHtml(string url)
        {
            var run = Task.Run(() => GetHtmlAsync(url));
            Task.WaitAll(run);
            return run.Result;
        }
        
        /// <summary>
        /// 获取指定url的dom
        /// </summary>
        public static async Task<string> GetHtmlAsync(string url)
        {
            if (_playwright is not null && _browserContext is not null && _page is not null &&  MemoryAnalyzer.NeedRestart())
            {
                await _page.CloseAsync();
                _page = null;
                await _browserContext.CloseAsync();
                await _browserContext.DisposeAsync();
                _browserContext = null;
                _playwright.Dispose();
                _playwright = null;
            }

            if (_page is { IsClosed: true })
                _page = null;
            if (_browserContext is { Browser.IsConnected: false })
                _browserContext = null;
            
            if (_browserContext == null || _page is null)
            {
                await InitAsync();
                // 由于不等待此调用，因此当前方法的执行将在调用完成之前继续。考虑对调用的结果应用“ wait”操作符。
                //throw new ArgumentNullException(nameof(_browser), "browser未初始化，需要先调用BrowserCollect.Init()方法");
            }
            var script = CollectScriptFactory.Instance.GetMatchedScript(url);
            var result = string.Empty;

            var flag = await CheckLogin();
            // File.WriteAllText("Verification-Code-Page", script.ToString());
            if (!flag)
            {
                var loginUrl = GetLoginUrl(url, result);
                await Login(loginUrl, new LoginOptions() { CheckLogin = CheckLogin });
            }
            await SyncCookies(_browserContext!);

            return result;

            async Task<bool> CheckLogin()
            {
                result = await script!.GetHtml(url, _page!);
                return !result.StartsWith(NeedLoginFlag);
            }

            static string GetLoginUrl(string originUrl, string response)
            {
                var loginUrl = response.StartsWith(NeedLoginFlag) ? response.Substring(NeedLoginFlag.Length) : "";
                return string.IsNullOrEmpty(loginUrl) ? originUrl : loginUrl;
            }

            static async Task SyncCookies(IBrowserContext context)
            {
                var cookies = await context.CookiesAsync();
                var syncCookies = new CookieContainer();
                foreach (var cookie in cookies)
                {
                    try
                    {
                        var value = cookie.Value.Contains(",") ? System.Net.WebUtility.UrlEncode(cookie.Value) : cookie.Value;
                        syncCookies.Add(new System.Net.Cookie(cookie.Name, value, cookie.Path, cookie.Domain));
                    } catch
                    {
                    }
                }
                //Global_Fun.Cookie = syncCookies;
            }
        }

        private static async Task Login(string url, LoginOptions options)
        {
            var shown = _isShow;
            Show();
            
            var page = await _browserContext!.NewPageAsync();
            // browser.Close += Close;
            page.Close += Close;

            var isClosed = false;
            
            await page.GotoAsync(url, new PageGotoOptions(){ WaitUntil = WaitUntilState.Commit});
            var checkClosed = Task.Run(() =>
            {
                var sw = Stopwatch.StartNew();
                // 浏览器或标签页被关闭，或超时30分钟
                while (!isClosed && sw.ElapsedMilliseconds <= 1800_000)
                {
                    Thread.Sleep(200);
                }
            });

            var tryCheckLogin = Task.Run(async () =>
            {
                var isLogin = false;
                var tempUrl = url;
                while (!isLogin)
                {
                    // 等待页面链接切换，及用户点击登录后跳转链接，超时10分钟
                    try
                    {
                        await page.MainFrame.WaitForNavigationAsync(new FrameWaitForNavigationOptions()
                            { Timeout = 600_000 });
                    }
                    catch
                    {
                        continue;
                    }
                    tempUrl = page.Url;
                    isLogin = await options.CheckLogin();
                }
            });
            Task.WaitAny(checkClosed, tryCheckLogin);

            if (!shown)
            {
                if (!page.IsClosed)
                {
                    await _browserContext.EnsureSessionCookieNotExpired();
                    await page.CloseAsync();
                }
                else if (_allCookies is not null)
                {
                    await _browserContext.EnsureSessionCookieNotExpired(_allCookies);
                }
                page = null;
                Hide();
            } 
            else
            {
                await page.CloseAsync();
            }
            
            void Close<T>(object obj, T type)
            {
                isClosed = true;
            }
        }

        /// <summary>
        /// 显示浏览器
        /// </summary>
        public static void Show()
        {
            if (!_isShow)
            {
                Destroy();
                var init = Task.Run(() => InitAsync(headless: false));
                Task.WaitAll(init);
                _isShow = true;
            }
        }

        /// <summary>
        /// 隐藏浏览器
        /// </summary>
        public static void Hide()
        {
            if (_isShow)
            {
                Destroy();
                var init = Task.Run(() => InitAsync(headless: true));
                Task.WaitAll(init);
                _isShow = false;
            }
        }

        /// <summary>
        /// 销毁playwright
        /// </summary>
        public static void Destroy()
        {
            if (_allCookies is not null)
                _browserContext?.EnsureSessionCookieNotExpired(_allCookies);
            _page?.CloseAsync().ConfigureAwait(false).GetAwaiter().GetResult();
            _page = null;
            _browserContext?.CloseAsync().Wait();
            _browserContext?.DisposeAsync().ConfigureAwait(true).GetAwaiter().GetResult();
            _browserContext = null;
        }
    }
}
